package com.science.slam.service.impl;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.PutObjectRequest;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.science.slam.domain.File;
import com.science.slam.domain.User;
import com.science.slam.exception.BusinessException;
import com.science.slam.exception.ErrorCode;
import com.science.slam.mapper.UserMapper;
import com.science.slam.request.EditFileMessageRequestBody;
import com.science.slam.service.FileService;
import com.science.slam.mapper.FileMapper;
import com.science.slam.service.UserService;
import com.science.slam.utils.IDUtils;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.List;

@Service
public class FileServiceImpl extends ServiceImpl<FileMapper, File>
    implements FileService{
    @Autowired
    OSS ossClient;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private FileMapper fileMapper;
    @Autowired
    private UserService userService;

    // Bucket名称
    String bucketName = "gaoyang-slam";
    @Override
    public String uploadToCloud(MultipartFile file, User user) {
        // 首先进行上传数据的转换
        String ID = IDUtils.uniqueFileID();
        String userName = user.getUserName();
        String account = user.getAccount();
        // 存储对象名称
        PutObjectRequest putObjectRequest = null;
        int retryCount = 0;
        while (retryCount < 3) {
            try {
                putObjectRequest = getPutObjectRequest(userName, ID, bucketName, file);
                break;
            } catch (IOException e) {
                ++retryCount;
                if (retryCount > 3) {
                    throw new BusinessException(ErrorCode.SERVER_ERROR);
                } else {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException ex) {
                        System.out.println("休眠失败");
                    }
                }
            }
        }
        ossClient.putObject(putObjectRequest); // 上传到服务器
        String url = "SLAM/" + userName + "/" + ID;
        Boolean result = saveFileToDatabase(ID, account, url);
        if (!result)
            throw new BusinessException(ErrorCode.DATABASE_ERROR);
        // 按照账户修改用户的持有文件数量
        Integer row = userService.addUserFileNumByAccount(account);
        if (row != 1)
            throw new BusinessException(ErrorCode.DATABASE_ERROR);
        user.setFileNumber(user.getFileNumber() + 1);
        // 关闭连接
        return url; // 返回对象名称
    }

    private Boolean saveFileToDatabase(String id, String ownerAccount, String url) {
        File file = new File(id, ownerAccount, url);
        int insertRow = fileMapper.insert(file);
        if (insertRow == 1)
            return true;
        return false;
    }

    private static @NotNull PutObjectRequest getPutObjectRequest(String userName,
                                                                 String ID,
                                                                 String bucketName,
                                                                 MultipartFile file)
            throws IOException
    {
        String objectName = "SLAM/" + userName + "/" + ID;
        PutObjectRequest putObjectRequest = null;
        putObjectRequest = new PutObjectRequest(bucketName, objectName,
                new ByteArrayInputStream(file.getBytes()));
        return putObjectRequest;
    }

    /**
     * 名为URL，实际是对象名称
     * @param objectName 对象名称
     * @param response 视频流
     * @return 下载结果
     */
    @Override
    public Boolean getVideoByURL(String objectName, HttpServletResponse response) {
        InputStream content = null;
        try {
            // 调用ossClient.getObject返回一个OSSObject实例，该实例包含文件内容及文件元数据。
            OSSObject ossObject = ossClient.getObject(bucketName, objectName);
            // 调用ossObject.getObjectContent获取文件输入流，可读取此输入流获取其内容。
            content = ossObject.getObjectContent();
            if (content != null) {
                response.setContentType("video/mp4");
                response.setCharacterEncoding("UTF-8");
                ServletOutputStream outputStream = response.getOutputStream();
                byte[] bytes = new byte[4096 * 4];
                int byteReadNum = 0;
                while ((byteReadNum = content.read(bytes)) != -1) {
                    outputStream.write(bytes, 0, byteReadNum);
                }
                outputStream.flush();
                content.close();
                outputStream.close();
            }
        } catch (OSSException oe) {
            System.out.println(oe.getMessage());
        } catch (ClientException | IOException ce) {
            System.out.println(ce.getMessage());
        }
        return true;
    }

    @Override
    public Boolean checkDownloadAuthorization(String url, User user) {
        QueryWrapper<File> fileQueryWrapper = new QueryWrapper<>();
        fileQueryWrapper.eq("file_url", url);
        File file = fileMapper.selectOne(fileQueryWrapper);
        if (file.getPublicType() != 0) 
            return true;
        return user.getAccount().equals(file.getOwnerAccount());
    }

    @Override
    public Boolean checkDeleteAuthorization(String url, User user) {
        QueryWrapper<File> fileQueryWrapper = new QueryWrapper<>();
        fileQueryWrapper.eq("file_url", url);
        File file = fileMapper.selectOne(fileQueryWrapper);
        if (file == null)
            return false;
        String ownerAccount = file.getOwnerAccount();
        String account = user.getAccount();
        if (ownerAccount.equals(account))
            return true;
        return false;
    }

    @Override
    public Boolean checkEditAuthorization(String url, User user, String messageName) {
        if (user.getIsAdmin() == 1)
            return true;
        if (messageName.equals("id") || messageName.equals("like_number") || messageName.equals("look_number"))
            return false;
        QueryWrapper<File> QueryWrapper = new QueryWrapper<>();
        QueryWrapper.eq("file_url", url);
        File file = fileMapper.selectOne(QueryWrapper);
        if (file == null)
            return false;
        String Account = file.getOwnerAccount();
        String account = user.getAccount();
        if (Account.equals(account))
            return true;
        return false;
    }

    @Override
    public List<File> getAllFiles(String account) {
        QueryWrapper<File> fileQueryWrapper = new QueryWrapper<>();
        fileQueryWrapper.eq("owner_account", account);
        return fileMapper.selectList(fileQueryWrapper);
    }

    @Override
    public Boolean deleteFileByUrl(String url) {
        try {
            // 删除文件
            ossClient.deleteObject(bucketName, url);
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
            return false;
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
            return false;
        }
        return true;
    }

    @Override
    public Boolean deleteFromDatabase(String url) {
        UpdateWrapper<File> fileUpdateWrapper = new UpdateWrapper<>();
        fileUpdateWrapper.eq("file_url", url);
        int delete = fileMapper.delete(fileUpdateWrapper);
        if (delete != 1)
            return false;
        return true;
    }

    /**
     * 按照给定的属性名称对文件进行修改
     * @param url 文件的url
     * @param body 包含message和messageName的对象
     * @return 是否修改成功
     */
    @Override
    public Boolean editFileMessage(String url, EditFileMessageRequestBody body) {
        String messageName = toUnderscoreStyle(body.getMessageName());
        UpdateWrapper<File> fileUpdateWrapper = new UpdateWrapper<>();
        Object message = body.getMessage();
        fileUpdateWrapper.eq("file_url", url);
        fileUpdateWrapper.set(messageName, message);
        int update = fileMapper.update(fileUpdateWrapper);
        if (update == 0)
            return false;
        return true;
    }

    /**
     * 小驼峰转下划线风格
     * @param colName 驼峰风格字符串
     * @return 下划线风格
     */
    public String toUnderscoreStyle(String colName) {
        StringBuilder ans = new StringBuilder();
        int n = colName.length();
        for (int i = 0; i < n; i++) {
            if (colName.charAt(i) >= 'A' && colName.charAt(i) <= 'Z') {
                ans.append('_');
                ans.append((char) (colName.charAt(i) - 'A' + 'a'));
                continue;
            }
            ans.append(colName.charAt(i));
        }
        return ans.toString();
    }

    @Override
    public List<File> searchByTags(List<String> tags) {
        QueryWrapper<File> fileQueryWrapper = new QueryWrapper<>();
        for (String tag : tags) {
            fileQueryWrapper = fileQueryWrapper.like("tags", tag);
        }
        fileQueryWrapper.eq("public_type", 1);
        return fileMapper.selectList(fileQueryWrapper);
    }

    @Override
    public File searchByID(String id) {
        QueryWrapper<File> fileQueryWrapper = new QueryWrapper<>();
        fileQueryWrapper.eq("id", id);
        return fileMapper.selectOne(fileQueryWrapper);
    }

    @Override
    public List<File> getAllPublicFiles() {
        QueryWrapper<File> fileQueryWrapper = new QueryWrapper<>();
        fileQueryWrapper.eq("public_type", 1);
        return fileMapper.selectList(fileQueryWrapper);
    }

    @Override
    public Integer incrementLookNumber(String fileID) {
        return incrementProp(fileID, "look_number");
    }

    @Override
    public Integer incrementLikeNumber(String fileID) {
        return incrementProp(fileID, "like_number");
    }

    @Override
    public Integer decrementLikeNumber(String fileID) {
        return decrementProp(fileID, "like_number");
    }

    private Integer decrementProp(String fileID, String PropName) {
        UpdateWrapper<File> fileUpdateWrapper = new UpdateWrapper<>();
        fileUpdateWrapper.eq("id", fileID);
        fileUpdateWrapper.setSql(PropName + " = " + PropName + " - 1");
        return fileMapper.update(fileUpdateWrapper);
    }

    private Integer incrementProp(String fileID, String PropName) {
        UpdateWrapper<File> fileUpdateWrapper = new UpdateWrapper<>();
        fileUpdateWrapper.eq("id", fileID);
        fileUpdateWrapper.setSql(PropName + " = " + PropName + " + 1");
        return fileMapper.update(fileUpdateWrapper);
    }
}